package com.woniuxy.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.woniuxy.entity.AllArticle;
import com.woniuxy.entity.ArticleComment;
import com.woniuxy.entity.DTO.algorithm.ContentInteractionPredictionDTO;
import com.woniuxy.service.IArticleCommentService;
import com.woniuxy.service.IArticleService;
import com.woniuxy.service.IContentInteractionPredictionService;
import com.woniuxy.util.ResponseData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author 陈华庆
 * @Date 2025/7/23 1:09
 * @Description TODO
 * 内容互动率预测服务实现
 * 基于历史数据的时间序列平滑法预测新内容7天内的浏览量和评论量
 */
@Service
public class ContentInteractionPredictionServiceImpl implements IContentInteractionPredictionService {

    @Autowired
    private IArticleService articleService;

    @Autowired
    private IArticleCommentService articleCommentService;

    @Override
    public ResponseData<List<ContentInteractionPredictionDTO>> predict7DayInteraction(Integer topicId) {
        // 获取过去90天内相同话题的文章数据作为样本
        LocalDate ninetyDaysAgo = LocalDate.now().minusDays(90);
        QueryWrapper<AllArticle> articleQueryWrapper = new QueryWrapper<>();
        articleQueryWrapper.eq("topic_id", topicId)
                .ge("create_time", ninetyDaysAgo)
                .eq("status", 2); // 只考虑已发布状态的文章

        List<AllArticle> sampleArticles = articleService.list(articleQueryWrapper);
        if (sampleArticles.isEmpty()) {
            return ResponseData.fail(500, "没有找到足够的历史数据进行预测");
        }

        // 计算每篇文章发布后7天内的浏览量和评论量
        List<ArticleStats> articleStatsList = sampleArticles.stream()
                .map(article -> calculateArticleStats(article))
                .collect(Collectors.toList());

        // 计算每天的平均增长率和标准差（用于置信区间）
        double[] dailyViewGrowthRates = calculateDailyGrowthRates(articleStatsList, true);
        double[] dailyCommentGrowthRates = calculateDailyGrowthRates(articleStatsList, false);
        double[] viewStdDevs = calculateDailyStdDevs(articleStatsList, true);
        double[] commentStdDevs = calculateDailyStdDevs(articleStatsList, false);

        // 预测未来7天的互动数据
        List<ContentInteractionPredictionDTO> predictions = new ArrayList<>();
        LocalDate today = LocalDate.now();
        double initialViewCount = calculateAverageInitialViews(articleStatsList);
        double initialCommentCount = calculateAverageInitialComments(articleStatsList);

        for (int i = 1; i <= 7; i++) {
            LocalDate predictDate = today.plusDays(i);
            int dayIndex = i - 1; // 数组索引从0开始

            // 计算预测值和置信区间（95%置信度对应1.96倍标准差）
            double predictedViews = initialViewCount * Math.exp(dailyViewGrowthRates[dayIndex] * i);
            double viewLowerBound = predictedViews - 1.96 * viewStdDevs[dayIndex];
            double viewUpperBound = predictedViews + 1.96 * viewStdDevs[dayIndex];

            double predictedComments = initialCommentCount * Math.exp(dailyCommentGrowthRates[dayIndex] * i);
            double commentLowerBound = predictedComments - 1.96 * commentStdDevs[dayIndex];
            double commentUpperBound = predictedComments + 1.96 * commentStdDevs[dayIndex];

            // 创建预测结果DTO
            ContentInteractionPredictionDTO prediction = new ContentInteractionPredictionDTO();
            prediction.setPredictDate(predictDate);
            prediction.setPredictedViewCount(Math.round(predictedViews));
            prediction.setViewCountLowerBound(Math.max(0, Math.round(viewLowerBound)));
            prediction.setViewCountUpperBound(Math.round(viewUpperBound));
            prediction.setPredictedCommentCount(Math.round(predictedComments));
            prediction.setCommentCountLowerBound(Math.max(0, Math.round(commentLowerBound)));
            prediction.setCommentCountUpperBound(Math.round(commentUpperBound));
            prediction.setSampleSize(articleStatsList.size());

            predictions.add(prediction);
        }

        return ResponseData.ok(predictions);
    }

    /**
     * 计算单篇文章的统计数据
     */
    private ArticleStats calculateArticleStats(AllArticle article) {
        ArticleStats stats = new ArticleStats();
        stats.setArticleId(article.getId());
        stats.setPublishDate(article.getCreateTime().toLocalDate());
        stats.setInitialViewCount(article.getViewCount() != null ? article.getViewCount() : 0L);

        // 计算7天内的评论数
        QueryWrapper<ArticleComment> commentQueryWrapper = new QueryWrapper<>();
        commentQueryWrapper.eq("article_id", article.getId())
                .ge("create_time", article.getCreateTime())
                .le("create_time", article.getCreateTime().plusDays(7))
                .eq("is_deleted", 0);
        long commentCount = articleCommentService.count(commentQueryWrapper);
        stats.setSevenDayCommentCount(commentCount);

        // 获取7天后的浏览量（这里简化处理，实际项目中可能需要历史数据记录）
        stats.setSevenDayViewCount(article.getViewCount() != null ? article.getViewCount() : 0L);

        return stats;
    }

    /**
     * 计算日增长率数组
     */
    private double[] calculateDailyGrowthRates(List<ArticleStats> statsList, boolean isViewMetric) {
        double[] growthRates = new double[7];
        for (int i = 0; i < 7; i++) {
            int day = i + 1;
            List<Double> dailyRates = statsList.stream()
                    .map(stat -> calculateDailyGrowthRate(stat, day, isViewMetric))
                    .filter(rate -> !Double.isNaN(rate) && !Double.isInfinite(rate))
                    .collect(Collectors.toList());

            // 使用平均增长率
            growthRates[i] = dailyRates.stream().mapToDouble(Double::doubleValue).average().orElse(0.1);
        }
        return growthRates;
    }

    /**
     * 计算单篇文章的日增长率
     */
    private double calculateDailyGrowthRate(ArticleStats stats, int day, boolean isViewMetric) {
        double initialValue = isViewMetric ? stats.getInitialViewCount() : 1; // 评论初始值设为1避免除零
        double dayValue = isViewMetric ? stats.getSevenDayViewCount() : stats.getSevenDayCommentCount();

        if (initialValue <= 0 || dayValue <= 0) {
            return 0.1; // 默认增长率10%
        }

        // 使用指数增长模型计算日均增长率
        return Math.log(dayValue / initialValue) / day;
    }

    /**
     * 计算日标准差数组
     */
    private double[] calculateDailyStdDevs(List<ArticleStats> statsList, boolean isViewMetric) {
        double[] stdDevs = new double[7];
        double[] averages = calculateDailyGrowthRates(statsList, isViewMetric);

        for (int i = 0; i < 7; i++) {
            int day = i + 1;
            List<Double> dailyRates = statsList.stream()
                    .map(stat -> calculateDailyGrowthRate(stat, day, isViewMetric))
                    .filter(rate -> !Double.isNaN(rate) && !Double.isInfinite(rate))
                    .collect(Collectors.toList());

            // 计算标准差
            // 将循环变量i对应的数组值存储到局部变量中
            double currentAverage = averages[i];
            double variance = dailyRates.stream()
                    .mapToDouble(rate -> Math.pow(rate - currentAverage, 2))
                    .average()
                    .orElse(0.05);
        }
        return stdDevs;
    }

    /**
     * 计算平均初始浏览量
     */
    private double calculateAverageInitialViews(List<ArticleStats> statsList) {
        return statsList.stream()
                .mapToDouble(stat -> stat.getInitialViewCount())
                .average()
                .orElse(10.0);
    }

    /**
     * 计算平均初始评论量
     */
    private double calculateAverageInitialComments(List<ArticleStats> statsList) {
        return statsList.stream()
                .mapToDouble(stat -> stat.getSevenDayCommentCount())
                .average()
                .orElse(2.0);
    }

    /**
     * 内部辅助类：文章统计数据
     */
    private static class ArticleStats {
        private Long articleId;
        private LocalDate publishDate;
        private Long initialViewCount;
        private Long sevenDayViewCount;
        private Long sevenDayCommentCount;

        // Getters and Setters
        public Long getArticleId() { return articleId; }
        public void setArticleId(Long articleId) { this.articleId = articleId; }
        public LocalDate getPublishDate() { return publishDate; }
        public void setPublishDate(LocalDate publishDate) { this.publishDate = publishDate; }
        public Long getInitialViewCount() { return initialViewCount; }
        public void setInitialViewCount(Long initialViewCount) { this.initialViewCount = initialViewCount; }
        public Long getSevenDayViewCount() { return sevenDayViewCount; }
        public void setSevenDayViewCount(Long sevenDayViewCount) { this.sevenDayViewCount = sevenDayViewCount; }
        public Long getSevenDayCommentCount() { return sevenDayCommentCount; }
        public void setSevenDayCommentCount(Long sevenDayCommentCount) { this.sevenDayCommentCount = sevenDayCommentCount; }
    }
}